今天我們要進入 React 16+ 最具革命性的底層重建——Fiber 架構 與 Concurrent Mode(並行模式)。
這是解答「為什麼 React 即使在處理龐大 UI 更新時依然能維持滑順流暢?」
在 React 15 及更早版本中,Reconciler(協調器)採用的是 Stack 演算法。它像是一條單行道,一旦觸發更新,就會透過遞迴同步呼叫所有組件:
[State 變更] ──► 同步遞迴走訪整個 VDOM 樹 ──► 渲染 DOM
└─── (期間主執行緒完全被佔用,不可中斷) ───┘
致命缺點:如果頁面的 VDOM 樹非常龐大,這段同步計算可能耗時 50ms~100ms 以上。
掉幀現象:瀏覽器為了維持 60fps 的流暢度,每 16.6ms 就必須進行一次畫面重繪與輸入響應。一旦 JavaScript 佔用主執行緒超過 16.6ms,使用者的打字、點擊或動畫就會出現嚴重的卡頓 (Jank)。
為了打破這個限制,React 團隊歷時數年重新撰寫了核心演算法,推出 Fiber 架構。
一種資料結構:取代了原本的 VDOM 節點。每個 Fiber 節點都是一個 JavaScript 物件,除了包含組件資訊外,還增加了 child(第一個子節點)、sibling(右側兄弟節點)、return(父節點) 等鏈表 (Linked List) 指標。
一個執行單元 (Unit of Work):將原本龐大的「一整棵樹的遞迴比對」,拆解成無數個「微小的 Fiber 節點比對」。
[Parent]
/ ▲
child return
/ \
[ChildA] ──sibling──► [ChildB]
因為改採用單向鏈表結構,React 不再需要依賴 JS 函數 call stack 的遞迴,而是可以用 while 迴圈隨時暫停、記錄進度,並在未來恢復執行
有了 Fiber 鏈表結構後,React 18 終於能夠完美支援 Concurrent Mode(並行模式)。
Concurrent Mode 的核心在於「時間切片 (Time Slicing)」與「優先級調度 (Priority Scheduling)」:
時間切片 (Time Slicing):React 將渲染任務切分成數個 5ms 左右的小分片。每執行完一個 Fiber 節點的比對,就會檢查瀏覽器是否有高優先級任務(如使用者點擊、鍵盤輸入)。
可中斷與恢復 (Interruptible Render):如果有高優先級任務,React 會立即暫停當前的背景渲染,將主執行緒交還給瀏覽器處理 UI 響應;待主執行緒空閒時,再繼續執行未完成的 Fiber 任務。
[Fiber Task 1] ──► [Fiber Task 2] ──► 🛑 收到使用者點擊事件!
│ (暫停 React 渲染,先處理 UI 響應)
▼
[完成點擊響應] ◄─────────────────────────┘
│
▼
[恢復執行 Fiber Task 3] ──► [Commit 到 Real DOM]
為了確保在「渲染可暫停、可放棄」的情況下,使用者不會看到只渲染了一半的半成品畫面,Fiber 採用了顯示卡常見的 雙緩衝技術 (Double Buffering):
current 樹:代表目前在屏幕上渲染的 Real DOM 對應樹。
workInProgress (wip) 樹:在背景記憶體中正在進行 Diffing 計算與構建的新樹。
所有的可中斷計算都在 workInProgress 樹上進行。只有當背景樹完全比對完畢,準備進入 Commit 階段時,React 才會在一瞬間將指標切換過去(current = workInProgress),一次性更新至 Real DOM!